package com.atguigu.c_designed.m3beheviorModel.b3Visitor;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

/**
 * 访问者模式被称为最复杂的设计模式，并且使用频率不高，设计模式的作者也评价为：大多情况下，你不需要使用访问者模式，但是一旦需要使用它时，那就真的需要使用了。
 * 访问者模式的基本思想是，针对系统中拥有固定类型数的对象结构（元素），在其内提供一个accept()方法用来接受访问者对象的访问。
 * 不同的访问者对同一元素的访问内容不同,使得相同的元素 集合可以产生不同的数据结果。
 * accept()方法可以接收不同的访问者对象，然后在内部将自己（元素）转发到接收到的访问者对象的visit（）方法内。
 * 访问者内部对应类型的visit（）方法就会得到回调执行，对元素进行操作。
 * 也就是通过两次动态分发（第一次是对访问者的分发accept()方法， 第二次是对元素的分发visit()方法），
 * 才最终将一个具体的元素传递到一个具体的访问者。如此 —来 ，就解耦了数据结构与操作，且数据操作不会改变元素状态。
 *
 * 访问者模式的核心是，解耦数据结构与数据操作，使得对元素的操作具备优秀的扩展性。可 以通过扩展不同的数据操作类型（访问者）实现对相同元素集的不同的操作。
 */
public class VisitorClient {
    public static void main(String[] args) {
        ObjectStruture objectStruture = new ObjectStruture();
        System.out.println("CTO查看报表");
        objectStruture.show(new CTOVisitor());
        System.out.println("CEO查看报表");
        objectStruture.show(new CEOVisitor());
    }
}

/**
 * 抽象访问者（Visitor）:接口或抽象类,
 * 该类定义了对每一个具体元素（Element ）的访问行为visit（）方法，其参数就是具体的元素（Element）对象。
 * 理论上来说,Visitor的方法个数与元素（Element）个数是相等的。
 * 如果元素（Element）个数经常变动，会导致Visitor的方法也要进行变动,此时，该情形并不适用访问者模式；
 */
//定义访问者接口
interface IVisitor{
    void visit(EngineerElement engineer);
    void visit(ManagerElement manager);
}
//具体访问者（ConcreteVisitor）:实现对具体元素的操作；
class CEOVisitor implements IVisitor{
    @Override
    public void visit(EngineerElement engineer) {
        System.out.println("工程师" +  engineer.name + "，KPI：" + engineer.kpi);
    }
    @Override
    public void visit(ManagerElement manager) {
        System.out.println("经理：" +  manager.name + ",KPI:" + manager.kpi + "，产品数量：" + manager.getProducts());
    }
}
//具体访问者（ConcreteVisitor）:实现对具体元素的操作；
class CTOVisitor implements IVisitor{

    @Override
    public void visit(EngineerElement engineer) {
        System.out.println("工程师" +  engineer.name + "，代码行数：" + engineer.getCodeLines());

    }
    @Override
    public void visit(ManagerElement manager) {
        System.out.println("经理：" +  manager.name + "，产品数量：" + manager.getProducts());
    }
}

/**
 * 抽象元素（Element ）:接口或抽象类，定义了一个接受访问者访问的方法accept() ,
 * 表示所有元素类型都支持被访问者访问；
 * 具体元素（Concrete Element ）:具体元素类型，提供接受访问者的具体实现。通常的实 现 都 为 ：visitor.visit（this）;
 */
//定义元素类
abstract class EmployeeElement{
    public String name;
    public int kpi;  //员工KPI
    public EmployeeElement(String name) {
        this.name = name;
        this.kpi = new Random().nextInt(10);
    }
    // 定义accept方法 接收访问者的访问
    public abstract void accept(IVisitor visitor);
}

class EngineerElement extends EmployeeElement{
    public EngineerElement(String name) {
        super(name);
    }
    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
    //考核指标是每年的代码量
    public int getCodeLines(){
        return new Random().nextInt(10* 10000);
    }
}
class ManagerElement extends EmployeeElement{
    public ManagerElement(String name) {
        super(name);
    }
    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
    //考核的是每年新产品研发数量
    public int getProducts(){
        return new Random().nextInt(10);
    }
}
//结构对象（ObjectStruture ）:该类内部维护了元素集合，并提供方法接受访问者对该集合所有元素进行操作。
class ObjectStruture{
    private List<EmployeeElement> employees = new LinkedList<>();

    public ObjectStruture() {
        employees.add(new ManagerElement("产品经理A"));
        employees.add(new EngineerElement("程序员A"));
        employees.add(new EngineerElement("程序员B"));
        employees.add(new EngineerElement("程序员C"));
        employees.add(new ManagerElement("产品经理B"));
        employees.add(new EngineerElement("程序员D"));
    }

    public void show(IVisitor visitor){
        for (EmployeeElement employee : employees) {
            employee.accept(visitor);
        }
    }
}
